Load libraries

library(Seurat)
library(dplyr)
library(RColorBrewer)
library(ggplot2)
library(ggExtra)
library(cowplot)
library(reticulate)
library(wesanderson)
use_python("/usr/bin/python3")

#Set ggplot theme as classic
theme_set(theme_classic())

This dataset was generated from the sequencing of two 10X V3 libraries run in parallel from the same tissue dissociation prep

Process the first library

Load the raw filtered matrix output from Cellranger

Countdata <- Read10X(data.dir = "../../RawData/Hem_1_filtered_feature_bc_matrix/")

Raw.data <- CreateSeuratObject(raw.data = Countdata,
                              min.cells = 3,
                              min.genes = 800,
                              project = "Hem1") ; rm(Countdata)

Raw.data@meta.data$Barcodes <- rownames(Raw.data@meta.data)

dim(Raw.data@data)
## [1] 18098 10180

Compute mito and ribo gene content per cell

mito.genes <- grep(pattern = "^mt-", x = rownames(x = Raw.data@data), value = TRUE)
percent.mito <- Matrix::colSums(Raw.data@raw.data[mito.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.mito, col.name = "percent.mito")

ribo.genes <- grep(pattern = "(^Rpl|^Rps|^Mrp)", x = rownames(x = Raw.data@data), value = TRUE)
percent.ribo <- Matrix::colSums(Raw.data@raw.data[ribo.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.ribo, col.name = "percent.ribo")

rm(mito.genes, percent.mito,ribo.genes,percent.ribo)
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

## Inspect cell based on relation between nUMI and nGene detected

# Relation between nUMI and nGene detected
Cell.QC.Stat <- Raw.data@meta.data
Cell.QC.Stat$Barcodes <- rownames(Cell.QC.Stat)

p1 <- ggplot(Cell.QC.Stat, aes(x=nUMI, y=nGene)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

Cells with deviating nGene/nUMI ratio display an Erythrocyte signature

genes.list <- list(c("Hbb-bt", "Hbq1a", "Isg20", "Fech", "Snca", "Rec114"))
enrich.name <- "Erythrocyte.signature"
Raw.data <- AddModuleScore(Raw.data,
                           genes.list = genes.list,
                           genes.pool = NULL,
                           n.bin = 5,
                           seed.use = 1,
                           ctrl.size = length(genes.list),
                           use.k = FALSE,
                           enrich.name = enrich.name,
                           random.seed = 1)

Cell.QC.Stat$Erythrocyte.signature <- Raw.data@meta.data$Erythrocyte.signature1
gradient <- colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)

p1 <- ggplot(Cell.QC.Stat, aes(x= log10(nUMI), y= log10(nGene))) +
      geom_point(aes(color= Erythrocyte.signature))  + 
      scale_color_gradientn(colours=rev(gradient), name='Erythrocyte score') +
      geom_smooth(method="lm")


p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")
p1

## Low quality cell filtering

Filtering cells based on percentage of mitochondrial transcripts

We applied a high and low median absolute deviation (mad) thresholds to exclude outlier cells

max.mito.thr <- median(Cell.QC.Stat$percent.mito) + 3*mad(Cell.QC.Stat$percent.mito)
min.mito.thr <- median(Cell.QC.Stat$percent.mito) - 3*mad(Cell.QC.Stat$percent.mito)
p1 <- ggplot(Cell.QC.Stat, aes(x=nGene, y=percent.mito)) +
  geom_point() +
  geom_hline(aes(yintercept = max.mito.thr), colour = "red", linetype = 2) +
  geom_hline(aes(yintercept = min.mito.thr), colour = "red", linetype = 2) +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[2])," cells removed\n",
                                         as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[1])," cells remain"),
           x = 6000, y = 0.4)

ggMarginal(p1, type = "histogram", fill="lightgrey", bins=100) 

# Filter cells based on these thresholds
Cell.QC.Stat <- Cell.QC.Stat %>% filter(percent.mito < max.mito.thr) %>% filter(percent.mito > min.mito.thr)

Filtering cells based on number of genes and transcripts detected

Remove cells with to few gene detected or with to many UMI counts

We filter cells which are likely to be doublet based on their higher content of transcript detected as well as cell with to few genes/UMI sequenced

# Set low and hight thresholds on the number of detected genes
min.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) - 3*mad(log10(Cell.QC.Stat$nGene))
max.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) + 3*mad(log10(Cell.QC.Stat$nGene))

# Set hight threshold on the number of transcripts
max.nUMI.thr <- median(log10(Cell.QC.Stat$nUMI)) + 3*mad(log10(Cell.QC.Stat$nUMI))
# Gene/UMI scatter plot before filtering
p1 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2)

ggMarginal(p1, type = "histogram", fill="lightgrey")

# Filter cells base on both metrics
Cell.QC.Stat <- Cell.QC.Stat %>% filter(log10(nGene) > min.Genes.thr) %>% filter(log10(nUMI) < max.nUMI.thr)

Filter cells below the main population nUMI/nGene relationship

lm.model <- lm(data = Cell.QC.Stat, formula = log10(nGene) ~ log10(nUMI))

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2) +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") +
  annotate(geom = "text", label = paste0(dim(Cell.QC.Stat)[1], " QC passed cells"), x = 4, y = 3.8)

ggMarginal(p2, type = "histogram", fill="lightgrey")

# Cells to exclude lie below an intercept offset of -0.09
Cell.QC.Stat$valideCells <- log10(Cell.QC.Stat$nGene) > (log10(Cell.QC.Stat$nUMI) * lm.model$coefficients[2] + (lm.model$coefficients[1] - 0.09))
p3 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point(aes(colour = valideCells)) +
  geom_smooth(method="lm") +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") + 
  theme(legend.position="none") +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$valideCells)[2]), " QC passed cells\n",
                                         as.numeric(table(Cell.QC.Stat$valideCells)[1]), " QC filtered"), x = 4, y = 3.8)

ggMarginal(p3, type = "histogram", fill="lightgrey")

# Remove invalid cells
Cell.QC.Stat <- Cell.QC.Stat %>% filter(valideCells)
Keep only the valid cells in the Seurat object
Raw.data <- SubsetData(Raw.data, cells.use = Cell.QC.Stat$Barcodes , subset.raw = T,  do.clean = F)
# Plot final QC metrics
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

p1 <- ggplot(Raw.data@meta.data, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
ggMarginal(p1, type = "histogram", fill="lightgrey")

rm(list = ls()[!ls() %in% "Raw.data"])

Use Scrublet to detect obvious doublets

Run Scrublet with default parameter

Export raw count matrix as input to Scrublet

#Export filtered matrix
dir.create("../../Scrublet_inputs")

exprData <- Matrix(as.matrix(Raw.data@raw.data), sparse = TRUE)
writeMM(exprData, "../../Scrublet_inputs/matrix1.mtx")
## NULL
import scrublet as scr
import scipy.io
import numpy as np
import os

#Load raw counts matrix and gene list
input_dir = '../../Scrublet_inputs'
counts_matrix = scipy.io.mmread(input_dir + '/matrix1.mtx').T.tocsc()

#Initialize Scrublet
scrub = scr.Scrublet(counts_matrix,
                     expected_doublet_rate=0.1,
                     sim_doublet_ratio=2,
                     n_neighbors = 8)

#Run the default pipeline
doublet_scores, predicted_doublets = scrub.scrub_doublets(min_counts=1, 
                                                          min_cells=3, 
                                                          min_gene_variability_pctl=85, 
                                                          n_prin_comps=25)
## Preprocessing...
## Simulating doublets...
## Embedding transcriptomes using PCA...
## Calculating doublet scores...
## Automatically set threshold at doublet score = 0.20
## Detected doublet rate = 7.4%
## Estimated detectable doublet fraction = 68.6%
## Overall doublet rate:
##  Expected   = 10.0%
##  Estimated  = 10.8%
## Elapsed time: 17.4 seconds
# Import scrublet's doublet score
Raw.data@meta.data$Doubletscore <- py$doublet_scores

# Plot doublet score
ggplot(Raw.data@meta.data, aes(x = Doubletscore, stat(ndensity))) +
  geom_histogram(bins = 200, colour ="lightgrey")+
  geom_vline(xintercept = 0.20, colour = "red", linetype = 2)

# Manually set threshold at doublet score to 0.2
Raw.data@meta.data$Predicted_doublets <- ifelse(py$doublet_scores > 0.2, "Doublet","Singlet" )
table(Raw.data@meta.data$Predicted_doublets)
## 
## Doublet Singlet 
##     658    8184

Filter doublets

#Remove Scrublet inferred doublets
Valid.Cells <- rownames(Raw.data@meta.data[Raw.data@meta.data$Predicted_doublets == "Singlet",])

QC.data.1 <- SubsetData(Raw.data,  cells.use = Valid.Cells, subset.raw = T, do.clean = F)
rm(list = ls()[!ls() %in% "QC.data.1"])

Process the second library

Load the raw filtered matrix output from Cellranger

Countdata <- Read10X(data.dir = "../../RawData/Hem_2_filtered_feature_bc_matrix/")

Raw.data <- CreateSeuratObject(raw.data = Countdata,
                              min.cells = 3,
                              min.genes = 800,
                              project = "Hem2") ; rm(Countdata)

Raw.data@meta.data$Barcodes <- rownames(Raw.data@meta.data)

dim(Raw.data@data)
## [1] 18048  8817

Compute mito and ribo gene content per cell

mito.genes <- grep(pattern = "^mt-", x = rownames(x = Raw.data@data), value = TRUE)
percent.mito <- Matrix::colSums(Raw.data@raw.data[mito.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.mito, col.name = "percent.mito")

ribo.genes <- grep(pattern = "(^Rpl|^Rps|^Mrp)", x = rownames(x = Raw.data@data), value = TRUE)
percent.ribo <- Matrix::colSums(Raw.data@raw.data[ribo.genes, ])/Matrix::colSums(Raw.data@raw.data)
Raw.data <- AddMetaData(object = Raw.data, metadata = percent.ribo, col.name = "percent.ribo")

rm(mito.genes, percent.mito,ribo.genes,percent.ribo)
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

Inspect cell based on relation between nUMI and nGene detected

# Relation between nUMI and nGene detected
Cell.QC.Stat <- Raw.data@meta.data
Cell.QC.Stat$Barcodes <- rownames(Cell.QC.Stat)

p1 <- ggplot(Cell.QC.Stat, aes(x=nUMI, y=nGene)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

Cells with deviating nGene/nUMI ratio display an Erythrocyte signature

genes.list <- list(c("Hbb-bt", "Hbq1a", "Isg20", "Fech", "Snca", "Rec114"))
enrich.name <- "Erythrocyte.signature"
Raw.data <- AddModuleScore(Raw.data,
                           genes.list = genes.list,
                           genes.pool = NULL,
                           n.bin = 5,
                           seed.use = 1,
                           ctrl.size = length(genes.list),
                           use.k = FALSE,
                           enrich.name = enrich.name,
                           random.seed = 1)

Cell.QC.Stat$Erythrocyte.signature <- Raw.data@meta.data$Erythrocyte.signature1
gradient <- colorRampPalette(brewer.pal(n =11, name = "Spectral"))(100)

p1 <- ggplot(Cell.QC.Stat, aes(x= log10(nUMI), y= log10(nGene))) +
      geom_point(aes(color= Erythrocyte.signature))  + 
      scale_color_gradientn(colours=rev(gradient), name='Erythrocyte score') +
      geom_smooth(method="lm")


p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")
p1

Low quality cell filtering

Filtering cells based on percentage of mitochondrial transcripts

We applied a high and low median absolute deviation (mad) thresholds to exclude outlier cells

max.mito.thr <- median(Cell.QC.Stat$percent.mito) + 3*mad(Cell.QC.Stat$percent.mito)
min.mito.thr <- median(Cell.QC.Stat$percent.mito) - 3*mad(Cell.QC.Stat$percent.mito)
p1 <- ggplot(Cell.QC.Stat, aes(x=nGene, y=percent.mito)) +
  geom_point() +
  geom_hline(aes(yintercept = max.mito.thr), colour = "red", linetype = 2) +
  geom_hline(aes(yintercept = min.mito.thr), colour = "red", linetype = 2) +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[2])," cells removed\n",
                                         as.numeric(table(Cell.QC.Stat$percent.mito > max.mito.thr | Cell.QC.Stat$percent.mito < min.mito.thr)[1])," cells remain"),
           x = 6000, y = 0.4)

ggMarginal(p1, type = "histogram", fill="lightgrey", bins=100) 

# Filter cells based on these thresholds
Cell.QC.Stat <- Cell.QC.Stat %>% filter(percent.mito < max.mito.thr) %>% filter(percent.mito > min.mito.thr)

Filtering cells based on number of genes and transcripts detected

Remove cells with to few gene detected or with to many UMI counts

We filter cells which are likely to be doublet based on their higher content of transcript detected as well as cell with to few genes/UMI sequenced

# Set low and hight thresholds on the number of detected genes
min.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) - 3*mad(log10(Cell.QC.Stat$nGene))
max.Genes.thr <- median(log10(Cell.QC.Stat$nGene)) + 3*mad(log10(Cell.QC.Stat$nGene))

# Set hight threshold on the number of transcripts
max.nUMI.thr <- median(log10(Cell.QC.Stat$nUMI)) + 3*mad(log10(Cell.QC.Stat$nUMI))
# Gene/UMI scatter plot before filtering
p1 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2)

ggMarginal(p1, type = "histogram", fill="lightgrey")

# Filter cells base on both metrics
Cell.QC.Stat <- Cell.QC.Stat %>% filter(log10(nGene) > min.Genes.thr) %>% filter(log10(nUMI) < max.nUMI.thr)

Filter cells below the main population nUMI/nGene relationship

lm.model <- lm(data = Cell.QC.Stat, formula = log10(nGene) ~ log10(nUMI))

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point() +
  geom_smooth(method="lm") +
  geom_hline(aes(yintercept = min.Genes.thr), colour = "green", linetype = 2) +
  geom_hline(aes(yintercept = max.Genes.thr), colour = "green", linetype = 2) +
  geom_vline(aes(xintercept = max.nUMI.thr), colour = "red", linetype = 2) +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") +
  annotate(geom = "text", label = paste0(dim(Cell.QC.Stat)[1], " QC passed cells"), x = 4, y = 3.8)

ggMarginal(p2, type = "histogram", fill="lightgrey")

# Cells to exclude lie below an intercept offset of -0.09
Cell.QC.Stat$valideCells <- log10(Cell.QC.Stat$nGene) > (log10(Cell.QC.Stat$nUMI) * lm.model$coefficients[2] + (lm.model$coefficients[1] - 0.09))
p3 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) +
  geom_point(aes(colour = valideCells)) +
  geom_smooth(method="lm") +
  geom_abline(intercept = lm.model$coefficients[1] - 0.09 , slope = lm.model$coefficients[2], color="orange") + 
  theme(legend.position="none") +
  annotate(geom = "text", label = paste0(as.numeric(table(Cell.QC.Stat$valideCells)[2]), " QC passed cells\n",
                                         as.numeric(table(Cell.QC.Stat$valideCells)[1]), " QC filtered"), x = 4, y = 3.8)

ggMarginal(p3, type = "histogram", fill="lightgrey")

# Remove invalid cells
Cell.QC.Stat <- Cell.QC.Stat %>% filter(valideCells)
Keep only the valid cells in the Seurat object
Raw.data <- SubsetData(Raw.data, cells.use = Cell.QC.Stat$Barcodes , subset.raw = T,  do.clean = F)
# Plot final QC metrics
VlnPlot(object = Raw.data, features.plot = c("nGene","nUMI", "percent.mito", "percent.ribo"), nCol = 2 )

p1 <- ggplot(Raw.data@meta.data, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
ggMarginal(p1, type = "histogram", fill="lightgrey")

rm(list = ls()[!ls() %in% c("Raw.data", "QC.data.1")])

Use Scrublet to detect obvious doublets

Run Scrublet with default parameter

Export raw count matrix as input to Scrublet

#Export filtered matrix
exprData <- Matrix(as.matrix(Raw.data@raw.data), sparse = TRUE)
writeMM(exprData, "../../Scrublet_inputs/matrix2.mtx")
## NULL
import scrublet as scr
import scipy.io
import numpy as np
import os

#Load raw counts matrix and gene list
input_dir = '../../Scrublet_inputs'
counts_matrix = scipy.io.mmread(input_dir + '/matrix2.mtx').T.tocsc()

#Initialize Scrublet
scrub = scr.Scrublet(counts_matrix,
                     expected_doublet_rate=0.1,
                     sim_doublet_ratio=2,
                     n_neighbors = 8)

#Run the default pipeline
doublet_scores, predicted_doublets = scrub.scrub_doublets(min_counts=1, 
                                                          min_cells=3, 
                                                          min_gene_variability_pctl=85, 
                                                          n_prin_comps=25)
## Preprocessing...
## Simulating doublets...
## Embedding transcriptomes using PCA...
## Calculating doublet scores...
## Automatically set threshold at doublet score = 0.24
## Detected doublet rate = 5.1%
## Estimated detectable doublet fraction = 59.0%
## Overall doublet rate:
##  Expected   = 10.0%
##  Estimated  = 8.7%
## Elapsed time: 13.5 seconds
## 
## /home/matthieu/.local/lib/python3.6/site-packages/scrublet/helper_functions.py:239: RuntimeWarning: invalid value encountered in log
##   gLog = lambda input: np.log(input[1] * np.exp(-input[0]) + input[2])
# Import scrublet's doublet score
Raw.data@meta.data$Doubletscore <- py$doublet_scores

# Plot doublet score
ggplot(Raw.data@meta.data, aes(x = Doubletscore, stat(ndensity))) +
  geom_histogram(bins = 200, colour ="lightgrey")+
  geom_vline(xintercept = 0.24, colour = "red", linetype = 2)

# Manually set threshold at doublet score to 0.2
Raw.data@meta.data$Predicted_doublets <- ifelse(py$doublet_scores > 0.24, "Doublet","Singlet" )
table(Raw.data@meta.data$Predicted_doublets)
## 
## Doublet Singlet 
##     385    7149

Filter doublets

#Remove Scrublet inferred doublets
Valid.Cells <- rownames(Raw.data@meta.data[Raw.data@meta.data$Predicted_doublets == "Singlet",])

QC.data.2 <- SubsetData(Raw.data,  cells.use = Valid.Cells, subset.raw = T, do.clean = F)
rm(list = ls()[!ls() %in% c("QC.data.1", "QC.data.2")])

Merge the two libraries

Hem.data <- MergeSeurat(QC.data.1, QC.data.2,
                        do.normalize = F,
                        add.cell.id1 = "Hem1",
                        add.cell.id2 = "Hem2")
Cell.QC.Stat <- Hem.data@meta.data
Cell.QC.Stat$Barcodes <- rownames(Cell.QC.Stat)

p1 <- ggplot(Cell.QC.Stat, aes(x=nUMI, y=nGene)) + geom_point() + geom_smooth(method="lm")
p1 <- ggMarginal(p1, type = "histogram", fill="lightgrey")

p2 <- ggplot(Cell.QC.Stat, aes(x=log10(nUMI), y=log10(nGene))) + geom_point() + geom_smooth(method="lm")
p2 <- ggMarginal(p2, type = "histogram", fill="lightgrey")

plot_grid(plotlist = list(p1,p2), ncol=2, align='h', rel_widths = c(1, 1)) ; rm(p1,p2)

rm(list = ls()[!ls() %in% "Hem.data"])

Filter gene expression matrix

# Filter genes expressed by less than 3 cells
num.cells <- Matrix::rowSums(Hem.data@data > 0)
genes.use <- names(x = num.cells[which(x = num.cells >= 3)])
Hem.data@raw.data <- Hem.data@raw.data[genes.use, ]
Hem.data@data <- Hem.data@data[genes.use, ]
# log-normalize the gene expression matrix
Hem.data<- NormalizeData(object = Hem.data,
                          normalization.method = "LogNormalize", 
                          scale.factor = round(median(Hem.data@meta.data$nUMI)),
                          display.progress = F)

Generate SRING dimentionality reduction

dir.create("../../SpringCoordinates")
# Export raw expression matrix and gene list to regenerate a spring plot
exprData <- Matrix(as.matrix(Hem.data@raw.data), sparse = TRUE)
writeMM(exprData, "../../SpringCoordinates/ExprData.mtx")
## NULL
Genelist <- row.names(Hem.data@raw.data)
write.table(Genelist, "../../SpringCoordinates/Genelist.csv", sep="\t", col.names = F, row.names = F)

Spring coordinates were generated using the online version of SPRING with the following parameters :

Number of cells: 15333
Number of genes that passed filter: 874
Min expressing cells (gene filtering): 3
Min number of UMIs (gene filtering): 3
Gene variability %ile (gene filtering): 95
Number of principal components: 20
Number of nearest neighbors: 8
Number of force layout iterations: 500

Import the new coordinates

# Import Spring coordinates
Coordinates <-read.table("../SpringCoordinates/hem_spring.csv", sep=",", header = T)
rownames(Coordinates) <- colnames(Hem.data@data)

Hem.data <- SetDimReduction(Hem.data,
                            reduction.type = "spring",
                            slot = "cell.embeddings",
                            new.data = as.matrix(Coordinates))

Hem.data@dr$spring@key <- "spring"
colnames(Hem.data@dr$spring@cell.embeddings) <- paste0(GetDimReduction(object= Hem.data, reduction.type = "spring",slot = "key"), c(1,2))

Assign cell state scores

Cell-Cycle Scores

s.genes <- c("Mcm5", "Pcna", "Tym5", "Fen1", "Mcm2", "Mcm4", "Rrm1", "Ung", "Gins2", "Mcm6", "Cdca7", "Dtl", "Prim1", "Uhrf1", "Mlf1ip", "Hells", "Rfc2", "Rap2", "Nasp", "Rad51ap1", "Gmnn", "Wdr76", "Slbp", "Ccne2", "Ubr7", "Pold3", "Msh2", "Atad2", "Rad51", "Rrm2", "Cdc45", "Cdc6", "Exo1", "Tipin", "Dscc1", "Blm", " Casp8ap2", "Usp1", "Clspn", "Pola1", "Chaf1b", "Brip1", "E2f8")
g2m.genes <- c("Hmgb2", "Ddk1","Nusap1", "Ube2c", "Birc5", "Tpx2", "Top2a", "Ndc80", "Cks2", "Nuf2", "Cks1b", "Mki67", "Tmpo", " Cenpk", "Tacc3", "Fam64a", "Smc4", "Ccnb2", "Ckap2l", "Ckap2", "Aurkb", "Bub1", "Kif11", "Anp32e", "Tubb4b", "Gtse1", "kif20b", "Hjurp", "Cdca3", "Hn1", "Cdc20", "Ttk", "Cdc25c", "kif2c", "Rangap1", "Ncapd2", "Dlgap5", "Cdca2", "Cdca8", "Ect2", "Kif23", "Hmmr", "Aurka", "Psrc1", "Anln", "Lbr", "Ckap5", "Cenpe", "Ctcf", "Nek2", "G2e3", "Gas2l3", "Cbx5", "Cenpa")

Hem.data <- CellCycleScoring(object = Hem.data,
                             s.genes = s.genes,
                             g2m.genes = g2m.genes,
                             set.ident = TRUE)
DimPlot(Hem.data,
        reduction.use = "spring",
        group.by = "Phase",
        cols.use = wes_palette("GrandBudapest1", 3, type = "discrete")[3:1],
        dim.1 = 1, 
        dim.2 = 2,
        do.label=T,
        label.size = 4,
        no.legend = F )

We assigned broad transcriptional cell state score based on known and manually curated marker genes

Apical progenitors

APgenes <- c("Rgcc", "Sparc", "Hes5","Hes1", "Slc1a3",
             "Ddah1", "Ldha", "Hmga2","Sfrp1", "Id4",
             "Creb5", "Ptn", "Lpar1", "Rcn1","Zfp36l1",
             "Sox9", "Sox2", "Nr2e1", "Ttyh1", "Trip6")
genes.list <- list(APgenes)
enrich.name <- "AP_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = APgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:20], ncol = 5)
Apical progenitors gene expression

Apical progenitors gene expression

Basal progenitors

BPgenes <- c("Eomes", "Igsf8", "Insm1", "Elavl2", "Elavl4",
             "Hes6","Gadd45g", "Neurog2", "Btg2", "Neurog1")
genes.list <- list(BPgenes)
enrich.name <- "BP_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = BPgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:10], ncol = 5)
Basal progenitors gene expression

Basal progenitors gene expression

Early pallial neurons

ENgenes <- c("Mfap4", "Nhlh2", "Nhlh1", "Ppp1r14a", "Nav1",
             "Neurod1", "Sorl1", "Svip", "Cxcl12", "Tenm4",
             "Dll3", "Rgmb", "Cntn2", "Vat1")
genes.list <- list(ENgenes)
enrich.name <- "EN_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = ENgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:14], ncol = 5)
Early pallial neurons gene expression

Early pallial neurons gene expression

Late pallial neurons

LNgenes <- c("Snhg11", "Pcsk1n", "Mapt", "Ina", "Stmn4",
             "Gap43", "Tubb2a", "Ly6h","Ptprd", "Mef2c")
genes.list <- list(LNgenes)
enrich.name <- "LN_signature"
Hem.data <- AddModuleScore(Hem.data,
                                  genes.list = genes.list,
                                  genes.pool = NULL,
                                  n.bin = 5,
                                  seed.use = 1,
                                  ctrl.size = length(genes.list),
                                  use.k = FALSE,
                                  enrich.name = enrich.name,
                                  random.seed = 1)
plot <- FeaturePlot(object = Hem.data,
                    features.plot = LNgenes,
                    cols.use = c("grey90", brewer.pal(9,"YlGnBu")),
                    reduction.use = "spring",
                    no.legend = T,
                    overlay = F,
                    dark.theme = F,
                    do.return =T,
                    no.axes = T)
for (i in 1:length(plot)){
  plot[[i]]$data <- plot[[i]]$data[order(plot[[i]]$data$gene),]
}
cowplot::plot_grid(plotlist = plot[1:10], ncol = 5)
Late pallial neurons gene expression

Late pallial neurons gene expression

FeaturePlot(object = Hem.data,
            features.plot = c("AP_signature1", "BP_signature1",
                              "EN_signature1", "LN_signature1"),
            cols.use = rev(brewer.pal(10,"Spectral")),
            reduction.use = "spring",
            no.legend = T,
            overlay = F,
            dark.theme = F,
            no.axes = T)

Save Seurat object

saveRDS(Hem.data, "../QC.filtered.cells.RDS")

Session Info

#date
format(Sys.time(), "%d %B, %Y, %H,%M")
## [1] "08 juin, 2021, 15,56"
#Packages used
sessionInfo()
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.5 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3.10.3
## LAPACK: /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3.10.3
## 
## locale:
##  [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=fr_FR.UTF-8        LC_COLLATE=fr_FR.UTF-8    
##  [5] LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
##  [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] wesanderson_0.3.6  reticulate_1.18    ggExtra_0.9        RColorBrewer_1.1-2
## [5] dplyr_1.0.6        Seurat_2.3.4       Matrix_1.2-17      cowplot_1.1.1     
## [9] ggplot2_3.3.3     
## 
## loaded via a namespace (and not attached):
##   [1] Rtsne_0.15          colorspace_2.0-1    ellipsis_0.3.2     
##   [4] class_7.3-17        modeltools_0.2-22   ggridges_0.5.1     
##   [7] mclust_5.4.5        htmlTable_1.13.2    base64enc_0.1-3    
##  [10] rstudioapi_0.11     proxy_0.4-23        farver_2.1.0       
##  [13] npsurv_0.4-0        flexmix_2.3-15      bit64_4.0.2        
##  [16] fansi_0.4.2         codetools_0.2-16    splines_3.6.3      
##  [19] R.methodsS3_1.7.1   lsei_1.2-0          robustbase_0.93-5  
##  [22] knitr_1.26          jsonlite_1.7.2      Formula_1.2-3      
##  [25] ica_1.0-2           cluster_2.1.0       kernlab_0.9-29     
##  [28] png_0.1-7           R.oo_1.23.0         shiny_1.4.0        
##  [31] compiler_3.6.3      httr_1.4.2          backports_1.1.5    
##  [34] fastmap_1.0.1       later_1.2.0         lars_1.2           
##  [37] acepack_1.4.1       htmltools_0.5.1.1   tools_3.6.3        
##  [40] igraph_1.2.5        gtable_0.3.0        glue_1.4.2         
##  [43] reshape2_1.4.3      RANN_2.6.1          rappdirs_0.3.1     
##  [46] Rcpp_1.0.6          vctrs_0.3.8         gdata_2.18.0       
##  [49] ape_5.3             nlme_3.1-141        iterators_1.0.12   
##  [52] fpc_2.2-3           lmtest_0.9-37       gbRd_0.4-11        
##  [55] xfun_0.18           stringr_1.4.0       mime_0.10          
##  [58] miniUI_0.1.1.1      lifecycle_1.0.0     irlba_2.3.3        
##  [61] gtools_3.8.1        DEoptimR_1.0-8      zoo_1.8-6          
##  [64] MASS_7.3-53         scales_1.1.1        promises_1.2.0.1   
##  [67] doSNOW_1.0.18       parallel_3.6.3      yaml_2.2.1         
##  [70] pbapply_1.4-2       gridExtra_2.3       segmented_1.0-0    
##  [73] rpart_4.1-15        latticeExtra_0.6-28 stringi_1.4.6      
##  [76] highr_0.8           foreach_1.4.7       checkmate_1.9.4    
##  [79] caTools_1.17.1.2    bibtex_0.4.2        Rdpack_0.11-0      
##  [82] SDMTools_1.1-221.1  rlang_0.4.11        pkgconfig_2.0.3    
##  [85] dtw_1.21-3          prabclus_2.3-1      bitops_1.0-6       
##  [88] evaluate_0.14       lattice_0.20-41     ROCR_1.0-7         
##  [91] purrr_0.3.4         labeling_0.4.2      htmlwidgets_1.5.3  
##  [94] bit_4.0.4           tidyselect_1.1.1    plyr_1.8.4         
##  [97] magrittr_2.0.1      R6_2.5.0            snow_0.4-3         
## [100] gplots_3.0.1.1      generics_0.1.0      Hmisc_4.3-0        
## [103] DBI_1.0.0           mgcv_1.8-33         pillar_1.6.1       
## [106] foreign_0.8-72      withr_2.4.2         mixtools_1.1.0     
## [109] fitdistrplus_1.0-14 survival_2.44-1.1   nnet_7.3-14        
## [112] tsne_0.1-3          tibble_3.1.2        crayon_1.4.1       
## [115] hdf5r_1.3.2.9000    KernSmooth_2.23-15  utf8_1.2.1         
## [118] rmarkdown_2.5       grid_3.6.3          data.table_1.14.0  
## [121] metap_1.1           digest_0.6.27       diptest_0.75-7     
## [124] xtable_1.8-4        httpuv_1.5.2        tidyr_1.1.3        
## [127] R.utils_2.9.0       stats4_3.6.3        munsell_0.5.0

  1. Institute of Psychiatry and Neuroscience of Paris, INSERM U1266, 75014, Paris, France, ↩︎

LS0tCnRpdGxlOiAiQ2VsbCBxdWFsaXR5IGNvbnRyb2wiCmF1dGhvcjoKICAgLSBNYXR0aGlldSBNb3JlYXVeW0luc3RpdHV0ZSBvZiBQc3ljaGlhdHJ5IGFuZCBOZXVyb3NjaWVuY2Ugb2YgUGFyaXMsIElOU0VSTSBVMTI2NiwgNzUwMTQsIFBhcmlzLCBGcmFuY2UsIG1hdHRoaWV1Lm1vcmVhdUBpbnNlcm0uZnJdIFshW10oaHR0cHM6Ly9vcmNpZC5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9pbWFnZXMvb3JjaWRfMTZ4MTYucG5nKV0oaHR0cHM6Ly9vcmNpZC5vcmcvMDAwMC0wMDAyLTI1OTItMjM3MykKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBkZl9wcmludDogdGliYmxlCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRoZW1lOiBjb3NtbwogICAgY3NzOiAiLi4vc3R5bGUuY3NzIgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGZpZy5hbGlnbiA9ICdjZW50ZXInLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFKQpgYGAKCiMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnRXh0cmEpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KHdlc2FuZGVyc29uKQp1c2VfcHl0aG9uKCIvdXNyL2Jpbi9weXRob24zIikKCiNTZXQgZ2dwbG90IHRoZW1lIGFzIGNsYXNzaWMKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKYGBgCgpUaGlzIGRhdGFzZXQgd2FzIGdlbmVyYXRlZCBmcm9tIHRoZSBzZXF1ZW5jaW5nIG9mIHR3byAxMFggVjMgbGlicmFyaWVzIHJ1biBpbiBwYXJhbGxlbCBmcm9tIHRoZSBzYW1lIHRpc3N1ZSBkaXNzb2NpYXRpb24gcHJlcCAKCiMgUHJvY2VzcyB0aGUgZmlyc3QgbGlicmFyeQoKIyMgTG9hZCB0aGUgcmF3IGZpbHRlcmVkIG1hdHJpeCBvdXRwdXQgZnJvbSBDZWxscmFuZ2VyCgpgYGB7cn0KQ291bnRkYXRhIDwtIFJlYWQxMFgoZGF0YS5kaXIgPSAiLi4vLi4vUmF3RGF0YS9IZW1fMV9maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC8iKQoKUmF3LmRhdGEgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KHJhdy5kYXRhID0gQ291bnRkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uY2VsbHMgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uZ2VuZXMgPSA4MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3QgPSAiSGVtMSIpIDsgcm0oQ291bnRkYXRhKQoKUmF3LmRhdGFAbWV0YS5kYXRhJEJhcmNvZGVzIDwtIHJvd25hbWVzKFJhdy5kYXRhQG1ldGEuZGF0YSkKCmRpbShSYXcuZGF0YUBkYXRhKQpgYGAKCgojIyBDb21wdXRlIG1pdG8gYW5kIHJpYm8gZ2VuZSBjb250ZW50IHBlciBjZWxsCgpgYGB7cn0KbWl0by5nZW5lcyA8LSBncmVwKHBhdHRlcm4gPSAiXm10LSIsIHggPSByb3duYW1lcyh4ID0gUmF3LmRhdGFAZGF0YSksIHZhbHVlID0gVFJVRSkKcGVyY2VudC5taXRvIDwtIE1hdHJpeDo6Y29sU3VtcyhSYXcuZGF0YUByYXcuZGF0YVttaXRvLmdlbmVzLCBdKS9NYXRyaXg6OmNvbFN1bXMoUmF3LmRhdGFAcmF3LmRhdGEpClJhdy5kYXRhIDwtIEFkZE1ldGFEYXRhKG9iamVjdCA9IFJhdy5kYXRhLCBtZXRhZGF0YSA9IHBlcmNlbnQubWl0bywgY29sLm5hbWUgPSAicGVyY2VudC5taXRvIikKCnJpYm8uZ2VuZXMgPC0gZ3JlcChwYXR0ZXJuID0gIiheUnBsfF5ScHN8Xk1ycCkiLCB4ID0gcm93bmFtZXMoeCA9IFJhdy5kYXRhQGRhdGEpLCB2YWx1ZSA9IFRSVUUpCnBlcmNlbnQucmlibyA8LSBNYXRyaXg6OmNvbFN1bXMoUmF3LmRhdGFAcmF3LmRhdGFbcmliby5nZW5lcywgXSkvTWF0cml4Ojpjb2xTdW1zKFJhdy5kYXRhQHJhdy5kYXRhKQpSYXcuZGF0YSA8LSBBZGRNZXRhRGF0YShvYmplY3QgPSBSYXcuZGF0YSwgbWV0YWRhdGEgPSBwZXJjZW50LnJpYm8sIGNvbC5uYW1lID0gInBlcmNlbnQucmlibyIpCgpybShtaXRvLmdlbmVzLCBwZXJjZW50Lm1pdG8scmliby5nZW5lcyxwZXJjZW50LnJpYm8pCmBgYAoKYGBge3IgZmlnLmRpbT1jKDUsIDQpfQpWbG5QbG90KG9iamVjdCA9IFJhdy5kYXRhLCBmZWF0dXJlcy5wbG90ID0gYygibkdlbmUiLCJuVU1JIiwgInBlcmNlbnQubWl0byIsICJwZXJjZW50LnJpYm8iKSwgbkNvbCA9IDIgKQpgYGAKIyMgSW5zcGVjdCBjZWxsIGJhc2VkIG9uIHJlbGF0aW9uIGJldHdlZW4gblVNSSBhbmQgbkdlbmUgZGV0ZWN0ZWQKCmBgYHtyIGZpZy5kaW09Yyg2LCAzLjUpfQojIFJlbGF0aW9uIGJldHdlZW4gblVNSSBhbmQgbkdlbmUgZGV0ZWN0ZWQKQ2VsbC5RQy5TdGF0IDwtIFJhdy5kYXRhQG1ldGEuZGF0YQpDZWxsLlFDLlN0YXQkQmFyY29kZXMgPC0gcm93bmFtZXMoQ2VsbC5RQy5TdGF0KQoKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9blVNSSwgeT1uR2VuZSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnAyIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAyIDwtIGdnTWFyZ2luYWwocDIsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnBsb3RfZ3JpZChwbG90bGlzdCA9IGxpc3QocDEscDIpLCBuY29sPTIsIGFsaWduPSdoJywgcmVsX3dpZHRocyA9IGMoMSwgMSkpIDsgcm0ocDEscDIpCmBgYApDZWxscyB3aXRoIGRldmlhdGluZyBuR2VuZS9uVU1JIHJhdGlvIGRpc3BsYXkgYW4gW0VyeXRocm9jeXRlIHNpZ25hdHVyZV0oaHR0cDovL21vdXNlYnJhaW4ub3JnL2RldmVsb3BtZW50L2NlbGx0eXBlcy5odG1sKQoKYGBge3J9CmdlbmVzLmxpc3QgPC0gbGlzdChjKCJIYmItYnQiLCAiSGJxMWEiLCAiSXNnMjAiLCAiRmVjaCIsICJTbmNhIiwgIlJlYzExNCIpKQplbnJpY2gubmFtZSA8LSAiRXJ5dGhyb2N5dGUuc2lnbmF0dXJlIgpSYXcuZGF0YSA8LSBBZGRNb2R1bGVTY29yZShSYXcuZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMubGlzdCA9IGdlbmVzLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuLmJpbiA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuayA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBlbnJpY2gubmFtZSA9IGVucmljaC5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCgpDZWxsLlFDLlN0YXQkRXJ5dGhyb2N5dGUuc2lnbmF0dXJlIDwtIFJhdy5kYXRhQG1ldGEuZGF0YSRFcnl0aHJvY3l0ZS5zaWduYXR1cmUxCmBgYAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpncmFkaWVudCA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwobiA9MTEsIG5hbWUgPSAiU3BlY3RyYWwiKSkoMTAwKQoKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9IGxvZzEwKG5VTUkpLCB5PSBsb2cxMChuR2VuZSkpKSArCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBFcnl0aHJvY3l0ZS5zaWduYXR1cmUpKSAgKyAKICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG91cnM9cmV2KGdyYWRpZW50KSwgbmFtZT0nRXJ5dGhyb2N5dGUgc2NvcmUnKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQoKCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKcDEKYGBgCiMjIExvdyBxdWFsaXR5IGNlbGwgZmlsdGVyaW5nIAoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMKCldlIGFwcGxpZWQgYSBoaWdoIGFuZCBsb3cgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAobWFkKSB0aHJlc2hvbGRzIHRvIGV4Y2x1ZGUgb3V0bGllciBjZWxscwoKYGBge3J9Cm1heC5taXRvLnRociA8LSBtZWRpYW4oQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykgKyAzKm1hZChDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvKQptaW4ubWl0by50aHIgPC0gbWVkaWFuKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8pIC0gMyptYWQoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PW5HZW5lLCB5PXBlcmNlbnQubWl0bykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXgubWl0by50aHIpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbi5taXRvLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPiBtYXgubWl0by50aHIgfCBDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvIDwgbWluLm1pdG8udGhyKVsyXSksIiBjZWxscyByZW1vdmVkXG4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWModGFibGUoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0byA+IG1heC5taXRvLnRociB8IENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPCBtaW4ubWl0by50aHIpWzFdKSwiIGNlbGxzIHJlbWFpbiIpLAogICAgICAgICAgIHggPSA2MDAwLCB5ID0gMC40KQoKZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiLCBiaW5zPTEwMCkgCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHBlcmNlbnQubWl0byA8IG1heC5taXRvLnRocikgJT4lIGZpbHRlcihwZXJjZW50Lm1pdG8gPiBtaW4ubWl0by50aHIpCmBgYAoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBudW1iZXIgb2YgZ2VuZXMgYW5kIHRyYW5zY3JpcHRzIGRldGVjdGVkCgojIyMjIFJlbW92ZSBjZWxscyB3aXRoIHRvIGZldyBnZW5lIGRldGVjdGVkIG9yIHdpdGggdG8gbWFueSBVTUkgY291bnRzCgpXZSBmaWx0ZXIgY2VsbHMgd2hpY2ggYXJlIGxpa2VseSB0byBiZSBkb3VibGV0IGJhc2VkIG9uIHRoZWlyIGhpZ2hlciBjb250ZW50IG9mIHRyYW5zY3JpcHQgZGV0ZWN0ZWQgYXMgd2VsbCBhcyBjZWxsIHdpdGggdG8gZmV3IGdlbmVzL1VNSSBzZXF1ZW5jZWQKCmBgYHtyfQojIFNldCBsb3cgYW5kIGhpZ2h0IHRocmVzaG9sZHMgb24gdGhlIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcwptaW4uR2VuZXMudGhyIDwtIG1lZGlhbihsb2cxMChDZWxsLlFDLlN0YXQkbkdlbmUpKSAtIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpCm1heC5HZW5lcy50aHIgPC0gbWVkaWFuKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpICsgMyptYWQobG9nMTAoQ2VsbC5RQy5TdGF0JG5HZW5lKSkKCiMgU2V0IGhpZ2h0IHRocmVzaG9sZCBvbiB0aGUgbnVtYmVyIG9mIHRyYW5zY3JpcHRzCm1heC5uVU1JLnRociA8LSBtZWRpYW4obG9nMTAoQ2VsbC5RQy5TdGF0JG5VTUkpKSArIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuVU1JKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CiMgR2VuZS9VTUkgc2NhdHRlciBwbG90IGJlZm9yZSBmaWx0ZXJpbmcKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXguR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1heC5uVU1JLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpCgpnZ01hcmdpbmFsKHAxLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYApgYGB7cn0KIyBGaWx0ZXIgY2VsbHMgYmFzZSBvbiBib3RoIG1ldHJpY3MKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKGxvZzEwKG5HZW5lKSA+IG1pbi5HZW5lcy50aHIpICU+JSBmaWx0ZXIobG9nMTAoblVNSSkgPCBtYXgublVNSS50aHIpCmBgYAoKIyMjIyBGaWx0ZXIgY2VsbHMgYmVsb3cgdGhlIG1haW4gcG9wdWxhdGlvbiBuVU1JL25HZW5lIHJlbGF0aW9uc2hpcAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpsbS5tb2RlbCA8LSBsbShkYXRhID0gQ2VsbC5RQy5TdGF0LCBmb3JtdWxhID0gbG9nMTAobkdlbmUpIH4gbG9nMTAoblVNSSkpCgpwMiA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1sb2cxMChuVU1JKSwgeT1sb2cxMChuR2VuZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtaW4uR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1heC5HZW5lcy50aHIpLCBjb2xvdXIgPSAiZ3JlZW4iLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWF4Lm5VTUkudGhyKSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGxtLm1vZGVsJGNvZWZmaWNpZW50c1sxXSAtIDAuMDkgLCBzbG9wZSA9IGxtLm1vZGVsJGNvZWZmaWNpZW50c1syXSwgY29sb3I9Im9yYW5nZSIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChkaW0oQ2VsbC5RQy5TdGF0KVsxXSwgIiBRQyBwYXNzZWQgY2VsbHMiKSwgeCA9IDQsIHkgPSAzLjgpCgpnZ01hcmdpbmFsKHAyLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYAoKYGBge3J9CiMgQ2VsbHMgdG8gZXhjbHVkZSBsaWUgYmVsb3cgYW4gaW50ZXJjZXB0IG9mZnNldCBvZiAtMC4wOQpDZWxsLlFDLlN0YXQkdmFsaWRlQ2VsbHMgPC0gbG9nMTAoQ2VsbC5RQy5TdGF0JG5HZW5lKSA+IChsb2cxMChDZWxsLlFDLlN0YXQkblVNSSkgKiBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0gKyAobG0ubW9kZWwkY29lZmZpY2llbnRzWzFdIC0gMC4wOSkpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpwMyA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1sb2cxMChuVU1JKSwgeT1sb2cxMChuR2VuZSkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gdmFsaWRlQ2VsbHMpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMV0gLSAwLjA5ICwgc2xvcGUgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0sIGNvbG9yPSJvcmFuZ2UiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCR2YWxpZGVDZWxscylbMl0pLCAiIFFDIHBhc3NlZCBjZWxsc1xuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCR2YWxpZGVDZWxscylbMV0pLCAiIFFDIGZpbHRlcmVkIiksIHggPSA0LCB5ID0gMy44KQoKZ2dNYXJnaW5hbChwMywgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpgYGAKYGBge3J9CiMgUmVtb3ZlIGludmFsaWQgY2VsbHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHZhbGlkZUNlbGxzKQpgYGAKCiMjIyMjIEtlZXAgb25seSB0aGUgdmFsaWQgY2VsbHMgaW4gdGhlIFNldXJhdCBvYmplY3QKCmBgYHtyfQpSYXcuZGF0YSA8LSBTdWJzZXREYXRhKFJhdy5kYXRhLCBjZWxscy51c2UgPSBDZWxsLlFDLlN0YXQkQmFyY29kZXMgLCBzdWJzZXQucmF3ID0gVCwgIGRvLmNsZWFuID0gRikKYGBgCgoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQojIFBsb3QgZmluYWwgUUMgbWV0cmljcwpWbG5QbG90KG9iamVjdCA9IFJhdy5kYXRhLCBmZWF0dXJlcy5wbG90ID0gYygibkdlbmUiLCJuVU1JIiwgInBlcmNlbnQubWl0byIsICJwZXJjZW50LnJpYm8iKSwgbkNvbCA9IDIgKQpgYGAKCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChSYXcuZGF0YUBtZXRhLmRhdGEsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCmdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSAiUmF3LmRhdGEiXSkKYGBgCgojIyBVc2UgU2NydWJsZXQgdG8gZGV0ZWN0IG9idmlvdXMgZG91YmxldHMKCiMjIyBSdW4gU2NydWJsZXQgd2l0aCBkZWZhdWx0IHBhcmFtZXRlcgoKRXhwb3J0IHJhdyBjb3VudCBtYXRyaXggYXMgaW5wdXQgdG8gU2NydWJsZXQKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KI0V4cG9ydCBmaWx0ZXJlZCBtYXRyaXgKZGlyLmNyZWF0ZSgiLi4vLi4vU2NydWJsZXRfaW5wdXRzIikKCmV4cHJEYXRhIDwtIE1hdHJpeChhcy5tYXRyaXgoUmF3LmRhdGFAcmF3LmRhdGEpLCBzcGFyc2UgPSBUUlVFKQp3cml0ZU1NKGV4cHJEYXRhLCAiLi4vLi4vU2NydWJsZXRfaW5wdXRzL21hdHJpeDEubXR4IikKYGBgCgpgYGB7cHl0aG9uIH0KaW1wb3J0IHNjcnVibGV0IGFzIHNjcgppbXBvcnQgc2NpcHkuaW8KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBvcwoKI0xvYWQgcmF3IGNvdW50cyBtYXRyaXggYW5kIGdlbmUgbGlzdAppbnB1dF9kaXIgPSAnLi4vLi4vU2NydWJsZXRfaW5wdXRzJwpjb3VudHNfbWF0cml4ID0gc2NpcHkuaW8ubW1yZWFkKGlucHV0X2RpciArICcvbWF0cml4MS5tdHgnKS5ULnRvY3NjKCkKCiNJbml0aWFsaXplIFNjcnVibGV0CnNjcnViID0gc2NyLlNjcnVibGV0KGNvdW50c19tYXRyaXgsCiAgICAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX2RvdWJsZXRfcmF0ZT0wLjEsCiAgICAgICAgICAgICAgICAgICAgIHNpbV9kb3VibGV0X3JhdGlvPTIsCiAgICAgICAgICAgICAgICAgICAgIG5fbmVpZ2hib3JzID0gOCkKCiNSdW4gdGhlIGRlZmF1bHQgcGlwZWxpbmUKZG91YmxldF9zY29yZXMsIHByZWRpY3RlZF9kb3VibGV0cyA9IHNjcnViLnNjcnViX2RvdWJsZXRzKG1pbl9jb3VudHM9MSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fY2VsbHM9MywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fZ2VuZV92YXJpYWJpbGl0eV9wY3RsPTg1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fcHJpbl9jb21wcz0yNSkKCgpgYGAKCmBgYHtyIGZpZy5kaW09Yyg0LCAzKX0KIyBJbXBvcnQgc2NydWJsZXQncyBkb3VibGV0IHNjb3JlClJhdy5kYXRhQG1ldGEuZGF0YSREb3VibGV0c2NvcmUgPC0gcHkkZG91YmxldF9zY29yZXMKCiMgUGxvdCBkb3VibGV0IHNjb3JlCmdncGxvdChSYXcuZGF0YUBtZXRhLmRhdGEsIGFlcyh4ID0gRG91YmxldHNjb3JlLCBzdGF0KG5kZW5zaXR5KSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwLCBjb2xvdXIgPSJsaWdodGdyZXkiKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjIwLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKQoKYGBgCgpgYGB7cn0KIyBNYW51YWxseSBzZXQgdGhyZXNob2xkIGF0IGRvdWJsZXQgc2NvcmUgdG8gMC4yClJhdy5kYXRhQG1ldGEuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMgPC0gaWZlbHNlKHB5JGRvdWJsZXRfc2NvcmVzID4gMC4yLCAiRG91YmxldCIsIlNpbmdsZXQiICkKdGFibGUoUmF3LmRhdGFAbWV0YS5kYXRhJFByZWRpY3RlZF9kb3VibGV0cykKYGBgCiMjIyBGaWx0ZXIgZG91YmxldHMKCmBgYHtyfQojUmVtb3ZlIFNjcnVibGV0IGluZmVycmVkIGRvdWJsZXRzClZhbGlkLkNlbGxzIDwtIHJvd25hbWVzKFJhdy5kYXRhQG1ldGEuZGF0YVtSYXcuZGF0YUBtZXRhLmRhdGEkUHJlZGljdGVkX2RvdWJsZXRzID09ICJTaW5nbGV0IixdKQoKUUMuZGF0YS4xIDwtIFN1YnNldERhdGEoUmF3LmRhdGEsICBjZWxscy51c2UgPSBWYWxpZC5DZWxscywgc3Vic2V0LnJhdyA9IFQsIGRvLmNsZWFuID0gRikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSAiUUMuZGF0YS4xIl0pCmBgYAoKCiMgUHJvY2VzcyB0aGUgc2Vjb25kIGxpYnJhcnkKCiMjIExvYWQgdGhlIHJhdyBmaWx0ZXJlZCBtYXRyaXggb3V0cHV0IGZyb20gQ2VsbHJhbmdlcgoKYGBge3J9CkNvdW50ZGF0YSA8LSBSZWFkMTBYKGRhdGEuZGlyID0gIi4uLy4uL1Jhd0RhdGEvSGVtXzJfZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXgvIikKClJhdy5kYXRhIDwtIENyZWF0ZVNldXJhdE9iamVjdChyYXcuZGF0YSA9IENvdW50ZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmNlbGxzID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLmdlbmVzID0gODAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0ID0gIkhlbTIiKSA7IHJtKENvdW50ZGF0YSkKClJhdy5kYXRhQG1ldGEuZGF0YSRCYXJjb2RlcyA8LSByb3duYW1lcyhSYXcuZGF0YUBtZXRhLmRhdGEpCgpkaW0oUmF3LmRhdGFAZGF0YSkKYGBgCiMjIENvbXB1dGUgbWl0byBhbmQgcmlibyBnZW5lIGNvbnRlbnQgcGVyIGNlbGwKCmBgYHtyfQptaXRvLmdlbmVzIDwtIGdyZXAocGF0dGVybiA9ICJebXQtIiwgeCA9IHJvd25hbWVzKHggPSBSYXcuZGF0YUBkYXRhKSwgdmFsdWUgPSBUUlVFKQpwZXJjZW50Lm1pdG8gPC0gTWF0cml4Ojpjb2xTdW1zKFJhdy5kYXRhQHJhdy5kYXRhW21pdG8uZ2VuZXMsIF0pL01hdHJpeDo6Y29sU3VtcyhSYXcuZGF0YUByYXcuZGF0YSkKUmF3LmRhdGEgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gUmF3LmRhdGEsIG1ldGFkYXRhID0gcGVyY2VudC5taXRvLCBjb2wubmFtZSA9ICJwZXJjZW50Lm1pdG8iKQoKcmliby5nZW5lcyA8LSBncmVwKHBhdHRlcm4gPSAiKF5ScGx8XlJwc3xeTXJwKSIsIHggPSByb3duYW1lcyh4ID0gUmF3LmRhdGFAZGF0YSksIHZhbHVlID0gVFJVRSkKcGVyY2VudC5yaWJvIDwtIE1hdHJpeDo6Y29sU3VtcyhSYXcuZGF0YUByYXcuZGF0YVtyaWJvLmdlbmVzLCBdKS9NYXRyaXg6OmNvbFN1bXMoUmF3LmRhdGFAcmF3LmRhdGEpClJhdy5kYXRhIDwtIEFkZE1ldGFEYXRhKG9iamVjdCA9IFJhdy5kYXRhLCBtZXRhZGF0YSA9IHBlcmNlbnQucmlibywgY29sLm5hbWUgPSAicGVyY2VudC5yaWJvIikKCnJtKG1pdG8uZ2VuZXMsIHBlcmNlbnQubWl0byxyaWJvLmdlbmVzLHBlcmNlbnQucmlibykKYGBgCgpgYGB7ciBmaWcuZGltPWMoNSwgNCl9ClZsblBsb3Qob2JqZWN0ID0gUmF3LmRhdGEsIGZlYXR1cmVzLnBsb3QgPSBjKCJuR2VuZSIsIm5VTUkiLCAicGVyY2VudC5taXRvIiwgInBlcmNlbnQucmlibyIpLCBuQ29sID0gMiApCmBgYAoKCiMjIEluc3BlY3QgY2VsbCBiYXNlZCBvbiByZWxhdGlvbiBiZXR3ZWVuIG5VTUkgYW5kIG5HZW5lIGRldGVjdGVkCgpgYGB7ciBmaWcuZGltPWMoNiwgMy41KX0KIyBSZWxhdGlvbiBiZXR3ZWVuIG5VTUkgYW5kIG5HZW5lIGRldGVjdGVkCkNlbGwuUUMuU3RhdCA8LSBSYXcuZGF0YUBtZXRhLmRhdGEKQ2VsbC5RQy5TdGF0JEJhcmNvZGVzIDwtIHJvd25hbWVzKENlbGwuUUMuU3RhdCkKCnAxIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PW5VTUksIHk9bkdlbmUpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQpwMSA8LSBnZ01hcmdpbmFsKHAxLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCgpwMiA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1sb2cxMChuVU1JKSwgeT1sb2cxMChuR2VuZSkpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQpwMiA8LSBnZ01hcmdpbmFsKHAyLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCgpwbG90X2dyaWQocGxvdGxpc3QgPSBsaXN0KHAxLHAyKSwgbmNvbD0yLCBhbGlnbj0naCcsIHJlbF93aWR0aHMgPSBjKDEsIDEpKSA7IHJtKHAxLHAyKQpgYGAKCkNlbGxzIHdpdGggZGV2aWF0aW5nIG5HZW5lL25VTUkgcmF0aW8gZGlzcGxheSBhbiBbRXJ5dGhyb2N5dGUgc2lnbmF0dXJlXShodHRwOi8vbW91c2VicmFpbi5vcmcvZGV2ZWxvcG1lbnQvY2VsbHR5cGVzLmh0bWwpCgpgYGB7cn0KZ2VuZXMubGlzdCA8LSBsaXN0KGMoIkhiYi1idCIsICJIYnExYSIsICJJc2cyMCIsICJGZWNoIiwgIlNuY2EiLCAiUmVjMTE0IikpCmVucmljaC5uYW1lIDwtICJFcnl0aHJvY3l0ZS5zaWduYXR1cmUiClJhdy5kYXRhIDwtIEFkZE1vZHVsZVNjb3JlKFJhdy5kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMucG9vbCA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG4uYmluID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjdHJsLnNpemUgPSBsZW5ndGgoZ2VuZXMubGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZS5rID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gMSkKCkNlbGwuUUMuU3RhdCRFcnl0aHJvY3l0ZS5zaWduYXR1cmUgPC0gUmF3LmRhdGFAbWV0YS5kYXRhJEVyeXRocm9jeXRlLnNpZ25hdHVyZTEKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CmdyYWRpZW50IDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbChuID0xMSwgbmFtZSA9ICJTcGVjdHJhbCIpKSgxMDApCgpwMSA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD0gbG9nMTAoblVNSSksIHk9IGxvZzEwKG5HZW5lKSkpICsKICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IEVyeXRocm9jeXRlLnNpZ25hdHVyZSkpICArIAogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3Vycz1yZXYoZ3JhZGllbnQpLCBuYW1lPSdFcnl0aHJvY3l0ZSBzY29yZScpICsKICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCgoKcDEgPC0gZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpwMQpgYGAKCiMjIExvdyBxdWFsaXR5IGNlbGwgZmlsdGVyaW5nIAoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBwZXJjZW50YWdlIG9mIG1pdG9jaG9uZHJpYWwgdHJhbnNjcmlwdHMKCldlIGFwcGxpZWQgYSBoaWdoIGFuZCBsb3cgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiAobWFkKSB0aHJlc2hvbGRzIHRvIGV4Y2x1ZGUgb3V0bGllciBjZWxscwoKYGBge3J9Cm1heC5taXRvLnRociA8LSBtZWRpYW4oQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykgKyAzKm1hZChDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvKQptaW4ubWl0by50aHIgPC0gbWVkaWFuKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8pIC0gMyptYWQoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0bykKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PW5HZW5lLCB5PXBlcmNlbnQubWl0bykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXgubWl0by50aHIpLCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbi5taXRvLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPiBtYXgubWl0by50aHIgfCBDZWxsLlFDLlN0YXQkcGVyY2VudC5taXRvIDwgbWluLm1pdG8udGhyKVsyXSksIiBjZWxscyByZW1vdmVkXG4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWModGFibGUoQ2VsbC5RQy5TdGF0JHBlcmNlbnQubWl0byA+IG1heC5taXRvLnRociB8IENlbGwuUUMuU3RhdCRwZXJjZW50Lm1pdG8gPCBtaW4ubWl0by50aHIpWzFdKSwiIGNlbGxzIHJlbWFpbiIpLAogICAgICAgICAgIHggPSA2MDAwLCB5ID0gMC40KQoKZ2dNYXJnaW5hbChwMSwgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiLCBiaW5zPTEwMCkgCmBgYAoKYGBge3J9CiMgRmlsdGVyIGNlbGxzIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHBlcmNlbnQubWl0byA8IG1heC5taXRvLnRocikgJT4lIGZpbHRlcihwZXJjZW50Lm1pdG8gPiBtaW4ubWl0by50aHIpCmBgYAoKIyMjIEZpbHRlcmluZyBjZWxscyBiYXNlZCBvbiBudW1iZXIgb2YgZ2VuZXMgYW5kIHRyYW5zY3JpcHRzIGRldGVjdGVkCgojIyMjIFJlbW92ZSBjZWxscyB3aXRoIHRvIGZldyBnZW5lIGRldGVjdGVkIG9yIHdpdGggdG8gbWFueSBVTUkgY291bnRzCgpXZSBmaWx0ZXIgY2VsbHMgd2hpY2ggYXJlIGxpa2VseSB0byBiZSBkb3VibGV0IGJhc2VkIG9uIHRoZWlyIGhpZ2hlciBjb250ZW50IG9mIHRyYW5zY3JpcHQgZGV0ZWN0ZWQgYXMgd2VsbCBhcyBjZWxsIHdpdGggdG8gZmV3IGdlbmVzL1VNSSBzZXF1ZW5jZWQKCmBgYHtyfQojIFNldCBsb3cgYW5kIGhpZ2h0IHRocmVzaG9sZHMgb24gdGhlIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcwptaW4uR2VuZXMudGhyIDwtIG1lZGlhbihsb2cxMChDZWxsLlFDLlN0YXQkbkdlbmUpKSAtIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpCm1heC5HZW5lcy50aHIgPC0gbWVkaWFuKGxvZzEwKENlbGwuUUMuU3RhdCRuR2VuZSkpICsgMyptYWQobG9nMTAoQ2VsbC5RQy5TdGF0JG5HZW5lKSkKCiMgU2V0IGhpZ2h0IHRocmVzaG9sZCBvbiB0aGUgbnVtYmVyIG9mIHRyYW5zY3JpcHRzCm1heC5uVU1JLnRociA8LSBtZWRpYW4obG9nMTAoQ2VsbC5RQy5TdGF0JG5VTUkpKSArIDMqbWFkKGxvZzEwKENlbGwuUUMuU3RhdCRuVU1JKSkKYGBgCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CiMgR2VuZS9VTUkgc2NhdHRlciBwbG90IGJlZm9yZSBmaWx0ZXJpbmcKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9bG9nMTAoblVNSSksIHk9bG9nMTAobkdlbmUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluLkdlbmVzLnRociksIGNvbG91ciA9ICJncmVlbiIsIGxpbmV0eXBlID0gMikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXguR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1heC5uVU1JLnRociksIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpCgpnZ01hcmdpbmFsKHAxLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYApgYGB7cn0KIyBGaWx0ZXIgY2VsbHMgYmFzZSBvbiBib3RoIG1ldHJpY3MKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKGxvZzEwKG5HZW5lKSA+IG1pbi5HZW5lcy50aHIpICU+JSBmaWx0ZXIobG9nMTAoblVNSSkgPCBtYXgublVNSS50aHIpCmBgYAoKIyMjIyBGaWx0ZXIgY2VsbHMgYmVsb3cgdGhlIG1haW4gcG9wdWxhdGlvbiBuVU1JL25HZW5lIHJlbGF0aW9uc2hpcAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpsbS5tb2RlbCA8LSBsbShkYXRhID0gQ2VsbC5RQy5TdGF0LCBmb3JtdWxhID0gbG9nMTAobkdlbmUpIH4gbG9nMTAoblVNSSkpCgpwMiA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1sb2cxMChuVU1JKSwgeT1sb2cxMChuR2VuZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtaW4uR2VuZXMudGhyKSwgY29sb3VyID0gImdyZWVuIiwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1heC5HZW5lcy50aHIpLCBjb2xvdXIgPSAiZ3JlZW4iLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWF4Lm5VTUkudGhyKSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gMikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGxtLm1vZGVsJGNvZWZmaWNpZW50c1sxXSAtIDAuMDkgLCBzbG9wZSA9IGxtLm1vZGVsJGNvZWZmaWNpZW50c1syXSwgY29sb3I9Im9yYW5nZSIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChkaW0oQ2VsbC5RQy5TdGF0KVsxXSwgIiBRQyBwYXNzZWQgY2VsbHMiKSwgeCA9IDQsIHkgPSAzLjgpCgpnZ01hcmdpbmFsKHAyLCB0eXBlID0gImhpc3RvZ3JhbSIsIGZpbGw9ImxpZ2h0Z3JleSIpCmBgYAoKYGBge3J9CiMgQ2VsbHMgdG8gZXhjbHVkZSBsaWUgYmVsb3cgYW4gaW50ZXJjZXB0IG9mZnNldCBvZiAtMC4wOQpDZWxsLlFDLlN0YXQkdmFsaWRlQ2VsbHMgPC0gbG9nMTAoQ2VsbC5RQy5TdGF0JG5HZW5lKSA+IChsb2cxMChDZWxsLlFDLlN0YXQkblVNSSkgKiBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0gKyAobG0ubW9kZWwkY29lZmZpY2llbnRzWzFdIC0gMC4wOSkpCmBgYAoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQpwMyA8LSBnZ3Bsb3QoQ2VsbC5RQy5TdGF0LCBhZXMoeD1sb2cxMChuVU1JKSwgeT1sb2cxMChuR2VuZSkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gdmFsaWRlQ2VsbHMpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMV0gLSAwLjA5ICwgc2xvcGUgPSBsbS5tb2RlbCRjb2VmZmljaWVudHNbMl0sIGNvbG9yPSJvcmFuZ2UiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9IHBhc3RlMChhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCR2YWxpZGVDZWxscylbMl0pLCAiIFFDIHBhc3NlZCBjZWxsc1xuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHRhYmxlKENlbGwuUUMuU3RhdCR2YWxpZGVDZWxscylbMV0pLCAiIFFDIGZpbHRlcmVkIiksIHggPSA0LCB5ID0gMy44KQoKZ2dNYXJnaW5hbChwMywgdHlwZSA9ICJoaXN0b2dyYW0iLCBmaWxsPSJsaWdodGdyZXkiKQpgYGAKYGBge3J9CiMgUmVtb3ZlIGludmFsaWQgY2VsbHMKQ2VsbC5RQy5TdGF0IDwtIENlbGwuUUMuU3RhdCAlPiUgZmlsdGVyKHZhbGlkZUNlbGxzKQpgYGAKCiMjIyMjIEtlZXAgb25seSB0aGUgdmFsaWQgY2VsbHMgaW4gdGhlIFNldXJhdCBvYmplY3QKCmBgYHtyfQpSYXcuZGF0YSA8LSBTdWJzZXREYXRhKFJhdy5kYXRhLCBjZWxscy51c2UgPSBDZWxsLlFDLlN0YXQkQmFyY29kZXMgLCBzdWJzZXQucmF3ID0gVCwgIGRvLmNsZWFuID0gRikKYGBgCgoKYGBge3IgZmlnLmRpbT1jKDQsIDQpfQojIFBsb3QgZmluYWwgUUMgbWV0cmljcwpWbG5QbG90KG9iamVjdCA9IFJhdy5kYXRhLCBmZWF0dXJlcy5wbG90ID0gYygibkdlbmUiLCJuVU1JIiwgInBlcmNlbnQubWl0byIsICJwZXJjZW50LnJpYm8iKSwgbkNvbCA9IDIgKQpgYGAKCgpgYGB7ciBmaWcuZGltPWMoNCwgNCl9CnAxIDwtIGdncGxvdChSYXcuZGF0YUBtZXRhLmRhdGEsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCmdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCJSYXcuZGF0YSIsICJRQy5kYXRhLjEiKV0pCmBgYAoKIyMgVXNlIFNjcnVibGV0IHRvIGRldGVjdCBvYnZpb3VzIGRvdWJsZXRzCgojIyMgUnVuIFNjcnVibGV0IHdpdGggZGVmYXVsdCBwYXJhbWV0ZXIKCkV4cG9ydCByYXcgY291bnQgbWF0cml4IGFzIGlucHV0IHRvIFNjcnVibGV0CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNFeHBvcnQgZmlsdGVyZWQgbWF0cml4CmV4cHJEYXRhIDwtIE1hdHJpeChhcy5tYXRyaXgoUmF3LmRhdGFAcmF3LmRhdGEpLCBzcGFyc2UgPSBUUlVFKQp3cml0ZU1NKGV4cHJEYXRhLCAiLi4vLi4vU2NydWJsZXRfaW5wdXRzL21hdHJpeDIubXR4IikKYGBgCgpgYGB7cHl0aG9uIH0KaW1wb3J0IHNjcnVibGV0IGFzIHNjcgppbXBvcnQgc2NpcHkuaW8KaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBvcwoKI0xvYWQgcmF3IGNvdW50cyBtYXRyaXggYW5kIGdlbmUgbGlzdAppbnB1dF9kaXIgPSAnLi4vLi4vU2NydWJsZXRfaW5wdXRzJwpjb3VudHNfbWF0cml4ID0gc2NpcHkuaW8ubW1yZWFkKGlucHV0X2RpciArICcvbWF0cml4Mi5tdHgnKS5ULnRvY3NjKCkKCiNJbml0aWFsaXplIFNjcnVibGV0CnNjcnViID0gc2NyLlNjcnVibGV0KGNvdW50c19tYXRyaXgsCiAgICAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX2RvdWJsZXRfcmF0ZT0wLjEsCiAgICAgICAgICAgICAgICAgICAgIHNpbV9kb3VibGV0X3JhdGlvPTIsCiAgICAgICAgICAgICAgICAgICAgIG5fbmVpZ2hib3JzID0gOCkKCiNSdW4gdGhlIGRlZmF1bHQgcGlwZWxpbmUKZG91YmxldF9zY29yZXMsIHByZWRpY3RlZF9kb3VibGV0cyA9IHNjcnViLnNjcnViX2RvdWJsZXRzKG1pbl9jb3VudHM9MSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fY2VsbHM9MywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fZ2VuZV92YXJpYWJpbGl0eV9wY3RsPTg1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fcHJpbl9jb21wcz0yNSkKCgpgYGAKCmBgYHtyIGZpZy5kaW09Yyg0LCAzKX0KIyBJbXBvcnQgc2NydWJsZXQncyBkb3VibGV0IHNjb3JlClJhdy5kYXRhQG1ldGEuZGF0YSREb3VibGV0c2NvcmUgPC0gcHkkZG91YmxldF9zY29yZXMKCiMgUGxvdCBkb3VibGV0IHNjb3JlCmdncGxvdChSYXcuZGF0YUBtZXRhLmRhdGEsIGFlcyh4ID0gRG91YmxldHNjb3JlLCBzdGF0KG5kZW5zaXR5KSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwLCBjb2xvdXIgPSJsaWdodGdyZXkiKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjI0LCBjb2xvdXIgPSAicmVkIiwgbGluZXR5cGUgPSAyKQoKYGBgCgpgYGB7cn0KIyBNYW51YWxseSBzZXQgdGhyZXNob2xkIGF0IGRvdWJsZXQgc2NvcmUgdG8gMC4yClJhdy5kYXRhQG1ldGEuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMgPC0gaWZlbHNlKHB5JGRvdWJsZXRfc2NvcmVzID4gMC4yNCwgIkRvdWJsZXQiLCJTaW5nbGV0IiApCnRhYmxlKFJhdy5kYXRhQG1ldGEuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMpCmBgYAoKIyMjIEZpbHRlciBkb3VibGV0cwoKYGBge3J9CiNSZW1vdmUgU2NydWJsZXQgaW5mZXJyZWQgZG91YmxldHMKVmFsaWQuQ2VsbHMgPC0gcm93bmFtZXMoUmF3LmRhdGFAbWV0YS5kYXRhW1Jhdy5kYXRhQG1ldGEuZGF0YSRQcmVkaWN0ZWRfZG91YmxldHMgPT0gIlNpbmdsZXQiLF0pCgpRQy5kYXRhLjIgPC0gU3Vic2V0RGF0YShSYXcuZGF0YSwgIGNlbGxzLnVzZSA9IFZhbGlkLkNlbGxzLCBzdWJzZXQucmF3ID0gVCwgZG8uY2xlYW4gPSBGKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoIlFDLmRhdGEuMSIsICJRQy5kYXRhLjIiKV0pCmBgYAoKCiMgTWVyZ2UgdGhlIHR3byBsaWJyYXJpZXMKCmBgYHtyfQpIZW0uZGF0YSA8LSBNZXJnZVNldXJhdChRQy5kYXRhLjEsIFFDLmRhdGEuMiwKICAgICAgICAgICAgICAgICAgICAgICAgZG8ubm9ybWFsaXplID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgYWRkLmNlbGwuaWQxID0gIkhlbTEiLAogICAgICAgICAgICAgICAgICAgICAgICBhZGQuY2VsbC5pZDIgPSAiSGVtMiIpCmBgYApgYGB7ciBmaWcuZGltPWMoNiwgMy41KX0KQ2VsbC5RQy5TdGF0IDwtIEhlbS5kYXRhQG1ldGEuZGF0YQpDZWxsLlFDLlN0YXQkQmFyY29kZXMgPC0gcm93bmFtZXMoQ2VsbC5RQy5TdGF0KQoKcDEgPC0gZ2dwbG90KENlbGwuUUMuU3RhdCwgYWVzKHg9blVNSSwgeT1uR2VuZSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAxIDwtIGdnTWFyZ2luYWwocDEsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnAyIDwtIGdncGxvdChDZWxsLlFDLlN0YXQsIGFlcyh4PWxvZzEwKG5VTUkpLCB5PWxvZzEwKG5HZW5lKSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpCnAyIDwtIGdnTWFyZ2luYWwocDIsIHR5cGUgPSAiaGlzdG9ncmFtIiwgZmlsbD0ibGlnaHRncmV5IikKCnBsb3RfZ3JpZChwbG90bGlzdCA9IGxpc3QocDEscDIpLCBuY29sPTIsIGFsaWduPSdoJywgcmVsX3dpZHRocyA9IGMoMSwgMSkpIDsgcm0ocDEscDIpCmBgYApgYGB7cn0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSAiSGVtLmRhdGEiXSkKYGBgCgojIyBGaWx0ZXIgZ2VuZSBleHByZXNzaW9uIG1hdHJpeAoKYGBge3J9CiMgRmlsdGVyIGdlbmVzIGV4cHJlc3NlZCBieSBsZXNzIHRoYW4gMyBjZWxscwpudW0uY2VsbHMgPC0gTWF0cml4Ojpyb3dTdW1zKEhlbS5kYXRhQGRhdGEgPiAwKQpnZW5lcy51c2UgPC0gbmFtZXMoeCA9IG51bS5jZWxsc1t3aGljaCh4ID0gbnVtLmNlbGxzID49IDMpXSkKSGVtLmRhdGFAcmF3LmRhdGEgPC0gSGVtLmRhdGFAcmF3LmRhdGFbZ2VuZXMudXNlLCBdCkhlbS5kYXRhQGRhdGEgPC0gSGVtLmRhdGFAZGF0YVtnZW5lcy51c2UsIF0KYGBgCgpgYGB7cn0KIyBsb2ctbm9ybWFsaXplIHRoZSBnZW5lIGV4cHJlc3Npb24gbWF0cml4CkhlbS5kYXRhPC0gTm9ybWFsaXplRGF0YShvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5mYWN0b3IgPSByb3VuZChtZWRpYW4oSGVtLmRhdGFAbWV0YS5kYXRhJG5VTUkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5LnByb2dyZXNzID0gRikKYGBgCgojIyBHZW5lcmF0ZSBTUklORyBkaW1lbnRpb25hbGl0eSByZWR1Y3Rpb24KCmBgYHtyfQpkaXIuY3JlYXRlKCIuLi8uLi9TcHJpbmdDb29yZGluYXRlcyIpCmBgYAoKYGBge3J9CiMgRXhwb3J0IHJhdyBleHByZXNzaW9uIG1hdHJpeCBhbmQgZ2VuZSBsaXN0IHRvIHJlZ2VuZXJhdGUgYSBzcHJpbmcgcGxvdApleHByRGF0YSA8LSBNYXRyaXgoYXMubWF0cml4KEhlbS5kYXRhQHJhdy5kYXRhKSwgc3BhcnNlID0gVFJVRSkKd3JpdGVNTShleHByRGF0YSwgIi4uLy4uL1NwcmluZ0Nvb3JkaW5hdGVzL0V4cHJEYXRhLm10eCIpCmBgYAoKYGBge3J9CkdlbmVsaXN0IDwtIHJvdy5uYW1lcyhIZW0uZGF0YUByYXcuZGF0YSkKd3JpdGUudGFibGUoR2VuZWxpc3QsICIuLi8uLi9TcHJpbmdDb29yZGluYXRlcy9HZW5lbGlzdC5jc3YiLCBzZXA9Ilx0IiwgY29sLm5hbWVzID0gRiwgcm93Lm5hbWVzID0gRikKYGBgCgoKU3ByaW5nIGNvb3JkaW5hdGVzIHdlcmUgZ2VuZXJhdGVkIHVzaW5nIHRoZSBvbmxpbmUgdmVyc2lvbiBvZiBbU1BSSU5HXShodHRwczovL2tsZWludG9vbHMuaG1zLmhhcnZhcmQuZWR1L3Rvb2xzL3NwcmluZy5odG1sKSB3aXRoIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyA6CgpgYGAKTnVtYmVyIG9mIGNlbGxzOiAxNTMzMwpOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzZWQgZmlsdGVyOiA4NzQKTWluIGV4cHJlc3NpbmcgY2VsbHMgKGdlbmUgZmlsdGVyaW5nKTogMwpNaW4gbnVtYmVyIG9mIFVNSXMgKGdlbmUgZmlsdGVyaW5nKTogMwpHZW5lIHZhcmlhYmlsaXR5ICVpbGUgKGdlbmUgZmlsdGVyaW5nKTogOTUKTnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzOiAyMApOdW1iZXIgb2YgbmVhcmVzdCBuZWlnaGJvcnM6IDgKTnVtYmVyIG9mIGZvcmNlIGxheW91dCBpdGVyYXRpb25zOiA1MDAKYGBgCgpJbXBvcnQgdGhlIG5ldyBjb29yZGluYXRlcwoKYGBge3J9CiMgSW1wb3J0IFNwcmluZyBjb29yZGluYXRlcwpDb29yZGluYXRlcyA8LXJlYWQudGFibGUoIi4uL1NwcmluZ0Nvb3JkaW5hdGVzL2hlbV9zcHJpbmcuY3N2Iiwgc2VwPSIsIiwgaGVhZGVyID0gVCkKcm93bmFtZXMoQ29vcmRpbmF0ZXMpIDwtIGNvbG5hbWVzKEhlbS5kYXRhQGRhdGEpCgpIZW0uZGF0YSA8LSBTZXREaW1SZWR1Y3Rpb24oSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24udHlwZSA9ICJzcHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvdCA9ICJjZWxsLmVtYmVkZGluZ3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmRhdGEgPSBhcy5tYXRyaXgoQ29vcmRpbmF0ZXMpKQoKSGVtLmRhdGFAZHIkc3ByaW5nQGtleSA8LSAic3ByaW5nIgpjb2xuYW1lcyhIZW0uZGF0YUBkciRzcHJpbmdAY2VsbC5lbWJlZGRpbmdzKSA8LSBwYXN0ZTAoR2V0RGltUmVkdWN0aW9uKG9iamVjdD0gSGVtLmRhdGEsIHJlZHVjdGlvbi50eXBlID0gInNwcmluZyIsc2xvdCA9ICJrZXkiKSwgYygxLDIpKQpgYGAKCgojIEFzc2lnbiBjZWxsIHN0YXRlIHNjb3JlcwoKIyMgQ2VsbC1DeWNsZSBTY29yZXMKCmBgYHtyfQpzLmdlbmVzIDwtIGMoIk1jbTUiLCAiUGNuYSIsICJUeW01IiwgIkZlbjEiLCAiTWNtMiIsICJNY200IiwgIlJybTEiLCAiVW5nIiwgIkdpbnMyIiwgIk1jbTYiLCAiQ2RjYTciLCAiRHRsIiwgIlByaW0xIiwgIlVocmYxIiwgIk1sZjFpcCIsICJIZWxscyIsICJSZmMyIiwgIlJhcDIiLCAiTmFzcCIsICJSYWQ1MWFwMSIsICJHbW5uIiwgIldkcjc2IiwgIlNsYnAiLCAiQ2NuZTIiLCAiVWJyNyIsICJQb2xkMyIsICJNc2gyIiwgIkF0YWQyIiwgIlJhZDUxIiwgIlJybTIiLCAiQ2RjNDUiLCAiQ2RjNiIsICJFeG8xIiwgIlRpcGluIiwgIkRzY2MxIiwgIkJsbSIsICIgQ2FzcDhhcDIiLCAiVXNwMSIsICJDbHNwbiIsICJQb2xhMSIsICJDaGFmMWIiLCAiQnJpcDEiLCAiRTJmOCIpCmcybS5nZW5lcyA8LSBjKCJIbWdiMiIsICJEZGsxIiwiTnVzYXAxIiwgIlViZTJjIiwgIkJpcmM1IiwgIlRweDIiLCAiVG9wMmEiLCAiTmRjODAiLCAiQ2tzMiIsICJOdWYyIiwgIkNrczFiIiwgIk1raTY3IiwgIlRtcG8iLCAiIENlbnBrIiwgIlRhY2MzIiwgIkZhbTY0YSIsICJTbWM0IiwgIkNjbmIyIiwgIkNrYXAybCIsICJDa2FwMiIsICJBdXJrYiIsICJCdWIxIiwgIktpZjExIiwgIkFucDMyZSIsICJUdWJiNGIiLCAiR3RzZTEiLCAia2lmMjBiIiwgIkhqdXJwIiwgIkNkY2EzIiwgIkhuMSIsICJDZGMyMCIsICJUdGsiLCAiQ2RjMjVjIiwgImtpZjJjIiwgIlJhbmdhcDEiLCAiTmNhcGQyIiwgIkRsZ2FwNSIsICJDZGNhMiIsICJDZGNhOCIsICJFY3QyIiwgIktpZjIzIiwgIkhtbXIiLCAiQXVya2EiLCAiUHNyYzEiLCAiQW5sbiIsICJMYnIiLCAiQ2thcDUiLCAiQ2VucGUiLCAiQ3RjZiIsICJOZWsyIiwgIkcyZTMiLCAiR2FzMmwzIiwgIkNieDUiLCAiQ2VucGEiKQoKSGVtLmRhdGEgPC0gQ2VsbEN5Y2xlU2NvcmluZyhvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzLmdlbmVzID0gcy5nZW5lcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnMm0uZ2VuZXMgPSBnMm0uZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0LmlkZW50ID0gVFJVRSkKCmBgYAoKYGBge3IgZmlnLmRpbT1jKDgsIDYpfQpEaW1QbG90KEhlbS5kYXRhLAogICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICBncm91cC5ieSA9ICJQaGFzZSIsCiAgICAgICAgY29scy51c2UgPSB3ZXNfcGFsZXR0ZSgiR3JhbmRCdWRhcGVzdDEiLCAzLCB0eXBlID0gImRpc2NyZXRlIilbMzoxXSwKICAgICAgICBkaW0uMSA9IDEsIAogICAgICAgIGRpbS4yID0gMiwKICAgICAgICBkby5sYWJlbD1ULAogICAgICAgIGxhYmVsLnNpemUgPSA0LAogICAgICAgIG5vLmxlZ2VuZCA9IEYgKQpgYGAKCldlIGFzc2lnbmVkIGJyb2FkIHRyYW5zY3JpcHRpb25hbCBjZWxsIHN0YXRlIHNjb3JlIGJhc2VkIG9uIGtub3duIGFuZCBtYW51YWxseSBjdXJhdGVkIG1hcmtlciBnZW5lcwoKIyMgQXBpY2FsIHByb2dlbml0b3JzCmBgYHtyfQpBUGdlbmVzIDwtIGMoIlJnY2MiLCAiU3BhcmMiLCAiSGVzNSIsIkhlczEiLCAiU2xjMWEzIiwKICAgICAgICAgICAgICJEZGFoMSIsICJMZGhhIiwgIkhtZ2EyIiwiU2ZycDEiLCAiSWQ0IiwKICAgICAgICAgICAgICJDcmViNSIsICJQdG4iLCAiTHBhcjEiLCAiUmNuMSIsIlpmcDM2bDEiLAogICAgICAgICAgICAgIlNveDkiLCAiU294MiIsICJOcjJlMSIsICJUdHloMSIsICJUcmlwNiIpCmdlbmVzLmxpc3QgPC0gbGlzdChBUGdlbmVzKQplbnJpY2gubmFtZSA8LSAiQVBfc2lnbmF0dXJlIgpIZW0uZGF0YSA8LSBBZGRNb2R1bGVTY29yZShIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLmxpc3QgPSBnZW5lcy5saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMucG9vbCA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLmJpbiA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdHJsLnNpemUgPSBsZW5ndGgoZ2VuZXMubGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuayA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5yaWNoLm5hbWUgPSBlbnJpY2gubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gMSkKYGBgCgpgYGB7ciBmaWcuc2hvdz0naGlkZScgfQpwbG90IDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzLnBsb3QgPSBBUGdlbmVzLAogICAgICAgICAgICAgICAgICAgIGNvbHMudXNlID0gYygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSksCiAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLnVzZSA9ICJzcHJpbmciLAogICAgICAgICAgICAgICAgICAgIG5vLmxlZ2VuZCA9IFQsCiAgICAgICAgICAgICAgICAgICAgb3ZlcmxheSA9IEYsCiAgICAgICAgICAgICAgICAgICAgZGFyay50aGVtZSA9IEYsCiAgICAgICAgICAgICAgICAgICAgZG8ucmV0dXJuID1ULAogICAgICAgICAgICAgICAgICAgIG5vLmF4ZXMgPSBUKQoKZm9yIChpIGluIDE6bGVuZ3RoKHBsb3QpKXsKICBwbG90W1tpXV0kZGF0YSA8LSBwbG90W1tpXV0kZGF0YVtvcmRlcihwbG90W1tpXV0kZGF0YSRnZW5lKSxdCn0KYGBgCgpgYGB7ciBmaWcuZGltPWMoNywgOS4zKSwgZmlnLmNhcD0gIkFwaWNhbCBwcm9nZW5pdG9ycyBnZW5lIGV4cHJlc3Npb24ifQpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90WzE6MjBdLCBuY29sID0gNSkKYGBgCgojIyBCYXNhbCBwcm9nZW5pdG9ycwoKYGBge3J9CkJQZ2VuZXMgPC0gYygiRW9tZXMiLCAiSWdzZjgiLCAiSW5zbTEiLCAiRWxhdmwyIiwgIkVsYXZsNCIsCiAgICAgICAgICAgICAiSGVzNiIsIkdhZGQ0NWciLCAiTmV1cm9nMiIsICJCdGcyIiwgIk5ldXJvZzEiKQpnZW5lcy5saXN0IDwtIGxpc3QoQlBnZW5lcykKZW5yaWNoLm5hbWUgPC0gIkJQX3NpZ25hdHVyZSIKSGVtLmRhdGEgPC0gQWRkTW9kdWxlU2NvcmUoSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5iaW4gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmsgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCmBgYAoKYGBge3IgZmlnLnNob3c9J2hpZGUnIH0KcGxvdCA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5wbG90ID0gQlBnZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDcpLCBmaWcuY2FwPSAiQmFzYWwgcHJvZ2VuaXRvcnMgZ2VuZSBleHByZXNzaW9uIn0KY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdFsxOjEwXSwgbmNvbCA9IDUpCmBgYAoKIyMgRWFybHkgcGFsbGlhbCBuZXVyb25zCgpgYGB7cn0KRU5nZW5lcyA8LSBjKCJNZmFwNCIsICJOaGxoMiIsICJOaGxoMSIsICJQcHAxcjE0YSIsICJOYXYxIiwKICAgICAgICAgICAgICJOZXVyb2QxIiwgIlNvcmwxIiwgIlN2aXAiLCAiQ3hjbDEyIiwgIlRlbm00IiwKICAgICAgICAgICAgICJEbGwzIiwgIlJnbWIiLCAiQ250bjIiLCAiVmF0MSIpCmdlbmVzLmxpc3QgPC0gbGlzdChFTmdlbmVzKQplbnJpY2gubmFtZSA8LSAiRU5fc2lnbmF0dXJlIgpIZW0uZGF0YSA8LSBBZGRNb2R1bGVTY29yZShIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLmxpc3QgPSBnZW5lcy5saXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMucG9vbCA9IE5VTEwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLmJpbiA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdHJsLnNpemUgPSBsZW5ndGgoZ2VuZXMubGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuayA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5yaWNoLm5hbWUgPSBlbnJpY2gubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmRvbS5zZWVkID0gMSkKYGBgCgpgYGB7ciBmaWcuc2hvdz0naGlkZScgfQpwbG90IDwtIEZlYXR1cmVQbG90KG9iamVjdCA9IEhlbS5kYXRhLAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzLnBsb3QgPSBFTmdlbmVzLAogICAgICAgICAgICAgICAgICAgIGNvbHMudXNlID0gYygiZ3JleTkwIiwgYnJld2VyLnBhbCg5LCJZbEduQnUiKSksCiAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uLnVzZSA9ICJzcHJpbmciLAogICAgICAgICAgICAgICAgICAgIG5vLmxlZ2VuZCA9IFQsCiAgICAgICAgICAgICAgICAgICAgb3ZlcmxheSA9IEYsCiAgICAgICAgICAgICAgICAgICAgZGFyay50aGVtZSA9IEYsCiAgICAgICAgICAgICAgICAgICAgZG8ucmV0dXJuID1ULAogICAgICAgICAgICAgICAgICAgIG5vLmF4ZXMgPSBUKQoKZm9yIChpIGluIDE6bGVuZ3RoKHBsb3QpKXsKICBwbG90W1tpXV0kZGF0YSA8LSBwbG90W1tpXV0kZGF0YVtvcmRlcihwbG90W1tpXV0kZGF0YSRnZW5lKSxdCn0KYGBgCgpgYGB7ciBmaWcuZGltPWMoNywgOC4zKSwgZmlnLmNhcD0gIkVhcmx5IHBhbGxpYWwgbmV1cm9ucyBnZW5lIGV4cHJlc3Npb24ifQpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90WzE6MTRdLCBuY29sID0gNSkKYGBgCgoKIyMgTGF0ZSBwYWxsaWFsIG5ldXJvbnMKCmBgYHtyfQpMTmdlbmVzIDwtIGMoIlNuaGcxMSIsICJQY3NrMW4iLCAiTWFwdCIsICJJbmEiLCAiU3RtbjQiLAogICAgICAgICAgICAgIkdhcDQzIiwgIlR1YmIyYSIsICJMeTZoIiwiUHRwcmQiLCAiTWVmMmMiKQpnZW5lcy5saXN0IDwtIGxpc3QoTE5nZW5lcykKZW5yaWNoLm5hbWUgPC0gIkxOX3NpZ25hdHVyZSIKSGVtLmRhdGEgPC0gQWRkTW9kdWxlU2NvcmUoSGVtLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lcy5saXN0ID0gZ2VuZXMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVzLnBvb2wgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5iaW4gPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZC51c2UgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3RybC5zaXplID0gbGVuZ3RoKGdlbmVzLmxpc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlLmsgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVucmljaC5uYW1lID0gZW5yaWNoLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb20uc2VlZCA9IDEpCmBgYAoKYGBge3IgZmlnLnNob3c9J2hpZGUnIH0KcGxvdCA8LSBGZWF0dXJlUGxvdChvYmplY3QgPSBIZW0uZGF0YSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5wbG90ID0gTE5nZW5lcywKICAgICAgICAgICAgICAgICAgICBjb2xzLnVzZSA9IGMoImdyZXk5MCIsIGJyZXdlci5wYWwoOSwiWWxHbkJ1IikpLAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbi51c2UgPSAic3ByaW5nIiwKICAgICAgICAgICAgICAgICAgICBuby5sZWdlbmQgPSBULAogICAgICAgICAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICAgICAgICAgIGRhcmsudGhlbWUgPSBGLAogICAgICAgICAgICAgICAgICAgIGRvLnJldHVybiA9VCwKICAgICAgICAgICAgICAgICAgICBuby5heGVzID0gVCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbG90KSl7CiAgcGxvdFtbaV1dJGRhdGEgPC0gcGxvdFtbaV1dJGRhdGFbb3JkZXIocGxvdFtbaV1dJGRhdGEkZ2VuZSksXQp9CmBgYAoKYGBge3IgZmlnLmRpbT1jKDcsIDcpLCBmaWcuY2FwPSAiTGF0ZSBwYWxsaWFsIG5ldXJvbnMgZ2VuZSBleHByZXNzaW9uIn0KY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdFsxOjEwXSwgbmNvbCA9IDUpCmBgYAoKCmBgYHtyIGZpZy5kaW09Yyg2LCA5KX0KRmVhdHVyZVBsb3Qob2JqZWN0ID0gSGVtLmRhdGEsCiAgICAgICAgICAgIGZlYXR1cmVzLnBsb3QgPSBjKCJBUF9zaWduYXR1cmUxIiwgIkJQX3NpZ25hdHVyZTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRU5fc2lnbmF0dXJlMSIsICJMTl9zaWduYXR1cmUxIiksCiAgICAgICAgICAgIGNvbHMudXNlID0gcmV2KGJyZXdlci5wYWwoMTAsIlNwZWN0cmFsIikpLAogICAgICAgICAgICByZWR1Y3Rpb24udXNlID0gInNwcmluZyIsCiAgICAgICAgICAgIG5vLmxlZ2VuZCA9IFQsCiAgICAgICAgICAgIG92ZXJsYXkgPSBGLAogICAgICAgICAgICBkYXJrLnRoZW1lID0gRiwKICAgICAgICAgICAgbm8uYXhlcyA9IFQpCmBgYAoKIyBTYXZlIFNldXJhdCBvYmplY3QKCmBgYHtyfQpzYXZlUkRTKEhlbS5kYXRhLCAiLi4vUUMuZmlsdGVyZWQuY2VsbHMuUkRTIikKYGBgCgoKIyBTZXNzaW9uIEluZm8KYGBge3J9CiNkYXRlCmZvcm1hdChTeXMudGltZSgpLCAiJWQgJUIsICVZLCAlSCwlTSIpCgojUGFja2FnZXMgdXNlZApzZXNzaW9uSW5mbygpCmBgYA==